package com.minelx.datamicroscope.core.query;

import com.alibaba.fastjson.JSONObject;
import com.minelx.datamicroscope.core.condition.KeyOperandCondition;
import com.minelx.datamicroscope.core.values.CommonTable;
import com.minelx.datamicroscope.core.values.CommonField;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.*;

public class QueryPathFactory {
	private final JSONObject data;

	private final List<Map.Entry<String, Object>> onEntries;

	private final CommonTable joinedTable;

	QueryPathFactory(JSONObject data) {
		this.data = data;

		onEntries = new ArrayList<>(data.getJSONObject("on").entrySet());
		joinedTable = CommonTable.from(data.getString("join"));
	}

	public String resourceName() {
		return data.getString("resourceName");
	}

	public Map<String, CommonField> connection() {
		return onEntries.stream().collect(toMap(
				Map.Entry::getKey,
				fieldName -> joinedTable.field(String.valueOf(fieldName.getValue()))));
	}

	public List<AliasedKey> collectedFields() {
		return convertToAliasedKeys(joinedTable, data.getJSONObject("collecting"));
	}

	public List<KeyOperandCondition> conditions() {
		return extractConditions(data, joinedTable);
	}

	public QueryPath createQueryPath() {
		return QueryPath.create(resourceName(), collectedFields(), connection(), conditions());
	}

	public static QueryPathFactory create(JSONObject data) {
		return new QueryPathFactory(data);
	}

	static List<AliasedKey> convertToAliasedKeys(CommonTable joinedTable, JSONObject collecting) {
		return collecting.entrySet().stream()
				.map(entry -> AliasedKey.create(entry.getKey(),
						joinedTable.field(entry.getValue().toString())))
				.collect(toList());
	}

	static List<KeyOperandCondition> extractConditions(JSONObject data, CommonTable joinedTable) {
		if (!data.containsKey("conditions")) {
			return Collections.emptyList();
		}
		List<JSONObject> conditionsAsJSON = data.getJSONArray("conditions").toJavaList(JSONObject.class);
		conditionsAsJSON.forEach(jsonObject -> jsonObject.put("key", joinedTable.path() + "." + jsonObject.getString("key")));
		return conditionsAsJSON.stream()
				.map(KeyOperandCondition::from)
				.collect(toList());
	}
}
